#include "oled.h"
#include <math.h>
#include <stdlib.h>
#include "font.h"
void OLED_I2C_GPIO_Init(void)
{
  RCC_APB2PeriphClockCmd(I2C_OLED_GPIO_CLK, ENABLE);

  GPIO_InitTypeDef GPIO_InitStructure;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Pin = I2C_OLED_SCL_PIN;
  GPIO_Init(I2C_OLED_GPIO_PORT, &GPIO_InitStructure);
  GPIO_InitStructure.GPIO_Pin = I2C_OLED_SDA_PIN;
  GPIO_Init(I2C_OLED_GPIO_PORT, &GPIO_InitStructure);
}

/**
 * @brief  I2C开始
 * @param  无
 * @retval 无
 */
void OLED_I2C_Start(void)
{
  OLED_W_SDA(1);
  OLED_W_SCL(1);
  OLED_W_SDA(0);
  OLED_W_SCL(0);
}

/**
 * @brief  I2C停止
 * @param  无
 * @retval 无
 */
void OLED_I2C_Stop(void)
{
  OLED_W_SDA(0);
  OLED_W_SCL(1);
  OLED_W_SDA(1);
}

/**
 * @brief  I2C发送一个字节
 * @param  Byte 要发送的一个字节
 * @retval 无
 */
void OLED_I2C_SendByte(uint8_t Byte)
{
  uint8_t i;
  for (i = 0; i < 8; i++)
  {
    OLED_W_SDA(Byte & (0x80 >> i));
    OLED_W_SCL(1);
    OLED_W_SCL(0);
  }
  OLED_W_SCL(1); // 额外的一个时钟，不处理应答信号
  OLED_W_SCL(0);
}

/**
 * @brief  OLED写命令
 * @param  Command 要写入的命令
 * @retval 无
 */
void OLED_WriteCommand(uint8_t Command)
{
  OLED_I2C_Start();
  OLED_I2C_SendByte(0x78); // 从机地址
  OLED_I2C_SendByte(0x00); // 写命令
  OLED_I2C_SendByte(Command);
  OLED_I2C_Stop();
}

/**
 * @brief  OLED写数据
 * @param  Data 要写入的数据
 * @retval 无
 */
void OLED_WriteData(uint8_t Data)
{
  OLED_I2C_Start();
  OLED_I2C_SendByte(0x78); // 从机地址
  OLED_I2C_SendByte(0x40); // 写数据
  OLED_I2C_SendByte(Data);
  OLED_I2C_Stop();
}

/**
 * @brief  OLED设置光标位置
 * @param  Y 以左上角为原点，向下方向的坐标，范围：0~7
 * @param  X 以左上角为原点，向右方向的坐标，范围：0~127
 * @retval 无
 */
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
  OLED_WriteCommand(0xB0 | Y);                 // 设置Y位置
  OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4)); // 设置X位置高4位
  OLED_WriteCommand(0x00 | (X & 0x0F));        // 设置X位置低4位
}

/**
 * @brief  OLED清屏
 * @param  无
 * @retval 无
 */
void OLED_Clear(void)
{
  uint8_t i, j;
  for (j = 0; j < 8; j++)
  {
    OLED_SetCursor(j, 0);
    for (i = 0; i < 128; i++)
    {
      OLED_WriteData(0x00);
    }
  }
}

/**
 * @brief  OLED显示一个字符
 * @param  Line 行位置，范围：1~4
 * @param  Column 列位置，范围：1~16
 * @param  Char 要显示的一个字符，范围：ASCII可见字符
 * @retval 无
 */
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{
  uint8_t i;
  OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8); // 设置光标位置在上半部分
  for (i = 0; i < 8; i++)
  {
    OLED_WriteData(OLED_F8x16[Char - ' '][i]); // 显示上半部分内容
  }
  OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8); // 设置光标位置在下半部分
  for (i = 0; i < 8; i++)
  {
    OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]); // 显示下半部分内容
  }
}

/**
 * @brief  OLED显示字符串
 * @param  Line 起始行位置，范围：1~4
 * @param  Column 起始列位置，范围：1~16
 * @param  String 要显示的字符串，范围：ASCII可见字符
 * @retval 无
 */
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
  uint8_t i;
  for (i = 0; String[i] != '\0'; i++)
  {
    OLED_ShowChar(Line, Column + i, String[i]);
  }
}

/**
 * @brief  OLED次方函数
 * @retval 返回值等于X的Y次方
 */
uint32_t OLED_Pow(uint32_t X, uint32_t Y)
{
  uint32_t Result = 1;
  while (Y--)
  {
    Result *= X;
  }
  return Result;
}

/**
 * @brief  OLED显示数字（十进制，正数）
 * @param  Line 起始行位置，范围：1~4
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~4294967295
 * @param  Length 要显示数字的长度，范围：1~10
 * @retval 无
 */
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
  uint8_t i;
  for (i = 0; i < Length; i++)
  {
    OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
  }
}

/**
 * @brief  OLED显示数字（十进制，带符号数）
 * @param  Line 起始行位置，范围：1~4
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：-2147483648~2147483647
 * @param  Length 要显示数字的长度，范围：1~10
 * @retval 无
 */
void OLED_ShowSignedNum(uint8_t Line, uint8_t Column, int32_t Number, uint8_t Length)
{
  uint8_t i;
  uint32_t Number1;
  if (Number >= 0)
  {
    OLED_ShowChar(Line, Column, '+');
    Number1 = Number;
  }
  else
  {
    OLED_ShowChar(Line, Column, '-');
    Number1 = -Number;
  }
  for (i = 0; i < Length; i++)
  {
    OLED_ShowChar(Line, Column + i + 1, Number1 / OLED_Pow(10, Length - i - 1) % 10 + '0');
  }
}

/**
 * @brief  OLED显示数字（十六进制，正数）
 * @param  Line 起始行位置，范围：1~4
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~0xFFFFFFFF
 * @param  Length 要显示数字的长度，范围：1~8
 * @retval 无
 */
void OLED_ShowHexNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
  uint8_t i, SingleNumber;
  for (i = 0; i < Length; i++)
  {
    SingleNumber = Number / OLED_Pow(16, Length - i - 1) % 16;
    if (SingleNumber < 10)
    {
      OLED_ShowChar(Line, Column + i, SingleNumber + '0');
    }
    else
    {
      OLED_ShowChar(Line, Column + i, SingleNumber - 10 + 'A');
    }
  }
}

/**
 * @brief  OLED显示数字（二进制，正数）
 * @param  Line 起始行位置，范围：1~4
 * @param  Column 起始列位置，范围：1~16
 * @param  Number 要显示的数字，范围：0~1111 1111 1111 1111
 * @param  Length 要显示数字的长度，范围：1~16
 * @retval 无
 */
void OLED_ShowBinNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
  uint8_t i;
  for (i = 0; i < Length; i++)
  {
    OLED_ShowChar(Line, Column + i, Number / OLED_Pow(2, Length - i - 1) % 2 + '0');
  }
}

/**
 * @brief  OLED初始化
 * @param  无
 * @retval 无
 */
void OLED_I2C_Display_Init(void)
{
  delay_ms(200);
  OLED_I2C_GPIO_Init();
  OLED_W_SCL(1);
  OLED_W_SDA(1);
  OLED_WriteCommand(0xAE); // 关闭显示
  OLED_WriteCommand(0xD5); // 设置显示时钟分频比/振荡器频率
  OLED_WriteCommand(0x80);
  OLED_WriteCommand(0xA8); // 设置多路复用率
  OLED_WriteCommand(0x3F);
  OLED_WriteCommand(0xD3); // 设置显示偏移
  OLED_WriteCommand(0x00);
  OLED_WriteCommand(0x40); // 设置显示开始行
  OLED_WriteCommand(0xA1); // 设置左右方向，0xA1正常 0xA0左右反置
  OLED_WriteCommand(0xC8); // 设置上下方向，0xC8正常 0xC0上下反置
  OLED_WriteCommand(0xDA); // 设置COM引脚硬件配置
  OLED_WriteCommand(0x12);
  OLED_WriteCommand(0x81); // 设置对比度控制
  OLED_WriteCommand(0xCF);
  OLED_WriteCommand(0xD9); // 设置预充电周期
  OLED_WriteCommand(0xF1);
  OLED_WriteCommand(0xDB); // 设置VCOMH取消选择级别
  OLED_WriteCommand(0x30);
  OLED_WriteCommand(0xA4); // 设置整个显示打开/关闭
  OLED_WriteCommand(0xA6); // 设置正常/倒转显示
  OLED_WriteCommand(0x8D); // 设置充电泵
  OLED_WriteCommand(0x14);
  OLED_WriteCommand(0xAF); // 开启显示
  OLED_Clear();            // OLED清屏
}

/**
 *
 *
 *
 */

// 定义颜色模式枚举
typedef enum
{
  OLED_COLOR_OFF = 0,
  OLED_COLOR_ON = 1
} OLED_ColorMode;

// 显存缓冲区，128列x8页，每页8行
static uint8_t oled_buffer[128][8];

/**
 * @brief  刷新OLED显示，将缓冲区内容写入OLED
 * @param  无
 * @retval 无
 */
void OLED_Refresh(void)
{
  for (uint8_t page = 0; page < 8; page++)
  {
    OLED_SetCursor(page, 0);
    OLED_I2C_Start();
    OLED_I2C_SendByte(0x78); // 从机地址
    OLED_I2C_SendByte(0x40); // 连续写入数据
    for (uint8_t col = 0; col < 128; col++)
    {
      OLED_I2C_SendByte(oled_buffer[col][page]);
    }
    OLED_I2C_Stop();
  }
}

/**
 * @brief  设置像素点状态
 * @param  x X坐标（0~127）
 * @param  y Y坐标（0~63）
 * @param  color 颜色模式（ON/OFF）
 * @retval 无
 */
void OLED_SetPixel(uint8_t x, uint8_t y, OLED_ColorMode color)
{
  if (x >= 128 || y >= 64)
    return;
  uint8_t page = y / 8;
  uint8_t bit_mask = 1 << (y % 8);
  if (color == OLED_COLOR_ON)
  {
    oled_buffer[x][page] |= bit_mask;
  }
  else
  {
    oled_buffer[x][page] &= ~bit_mask;
  }
}

/**
 * @brief  绘制线段（Bresenham算法）
 * @param  x1 起点X
 * @param  y1 起点Y
 * @param  x2 终点X
 * @param  y2 终点Y
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawLine(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, OLED_ColorMode color)
{
  int dx = abs(x2 - x1);
  int dy = abs(y2 - y1);
  int sx = (x1 < x2) ? 1 : -1;
  int sy = (y1 < y2) ? 1 : -1;
  int err = dx - dy;

  while (1)
  {
    OLED_SetPixel(x1, y1, color);
    if (x1 == x2 && y1 == y2)
      break;
    int e2 = 2 * err;
    if (e2 > -dy)
    {
      err -= dy;
      x1 += sx;
    }
    if (e2 < dx)
    {
      err += dx;
      y1 += sy;
    }
  }
}

/**
 * @brief  绘制矩形边框
 * @param  x 左上角X
 * @param  y 左上角Y
 * @param  w 宽度
 * @param  h 高度
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color)
{
  uint8_t x_end = x + w - 1;
  uint8_t y_end = y + h - 1;
  OLED_DrawLine(x, y, x_end, y, color);         // 上边
  OLED_DrawLine(x, y_end, x_end, y_end, color); // 下边
  OLED_DrawLine(x, y, x, y_end, color);         // 左边
  OLED_DrawLine(x_end, y, x_end, y_end, color); // 右边
}

/**
 * @brief  绘制填充矩形
 * @param  x 左上角X
 * @param  y 左上角Y
 * @param  w 宽度
 * @param  h 高度
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawFilledRectangle(uint8_t x, uint8_t y, uint8_t w, uint8_t h, OLED_ColorMode color)
{
  for (uint8_t i = y; i < y + h; i++)
  {
    OLED_DrawLine(x, i, x + w - 1, i, color);
  }
}

/**
 * @brief  绘制三角形边框
 * @param  x1 顶点1X
 * @param  y1 顶点1Y
 * @param  x2 顶点2X
 * @param  y2 顶点2Y
 * @param  x3 顶点3X
 * @param  y3 顶点3Y
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color)
{
  OLED_DrawLine(x1, y1, x2, y2, color);
  OLED_DrawLine(x2, y2, x3, y3, color);
  OLED_DrawLine(x3, y3, x1, y1, color);
}

/**
 * @brief  绘制填充三角形（扫描线算法）
 * @param  x1,y1,x2,y2,x3,y3 三角形顶点
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawFilledTriangle(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2, uint8_t x3, uint8_t y3, OLED_ColorMode color)
{
// 排序顶点按y坐标升序
#define SWAP(a, b) \
  {                \
    uint8_t t = a; \
    a = b;         \
    b = t;         \
  }
  if (y2 < y1)
  {
    SWAP(y1, y2);
    SWAP(x1, x2);
  }
  if (y3 < y1)
  {
    SWAP(y1, y3);
    SWAP(x1, x3);
  }
  if (y3 < y2)
  {
    SWAP(y2, y3);
    SWAP(x2, x3);
  }

  int dx13 = x3 - x1, dy13 = y3 - y1;
  int dx12 = x2 - x1, dy12 = y2 - y1;
  int dx23 = x3 - x2, dy23 = y3 - y2;

  for (uint8_t y = y1; y <= y3; y++)
  {
    uint8_t start_x, end_x;
    // 计算左右边界
    if (y < y2)
    {
      start_x = x1 + dx13 * (y - y1) / dy13;
      end_x = x1 + dx12 * (y - y1) / dy12;
    }
    else
    {
      start_x = x3 + dx23 * (y - y3) / dy23;
      end_x = x2 + dx12 * (y - y2) / dy12;
    }
    if (start_x > end_x)
      SWAP(start_x, end_x);
    OLED_DrawLine(start_x, y, end_x, y, color);
  }
}

/**
 * @brief  绘制圆（中点圆算法）
 * @param  x0 圆心X
 * @param  y0 圆心Y
 * @param  r 半径
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawCircle(uint8_t x0, uint8_t y0, uint8_t r, OLED_ColorMode color)
{
  int x = r, y = 0;
  int err = 0;

  while (x >= y)
  {
    OLED_SetPixel(x0 + x, y0 + y, color);
    OLED_SetPixel(x0 + y, y0 + x, color);
    OLED_SetPixel(x0 - y, y0 + x, color);
    OLED_SetPixel(x0 - x, y0 + y, color);
    OLED_SetPixel(x0 - x, y0 - y, color);
    OLED_SetPixel(x0 - y, y0 - x, color);
    OLED_SetPixel(x0 + y, y0 - x, color);
    OLED_SetPixel(x0 + x, y0 - y, color);

    if (err <= 0)
    {
      y++;
      err += 2 * y + 1;
    }
    if (err > 0)
    {
      x--;
      err -= 2 * x + 1;
    }
  }
}

/**
 * @brief  绘制填充圆
 * @param  x0,y0 圆心
 * @param  r 半径
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawFilledCircle(uint8_t x0, uint8_t y0, uint8_t r, OLED_ColorMode color)
{
  int x = r, y = 0;
  int err = 0;

  while (x >= y)
  {
    OLED_DrawLine(x0 - x, y0 + y, x0 + x, y0 + y, color);
    OLED_DrawLine(x0 - x, y0 - y, x0 + x, y0 - y, color);
    OLED_DrawLine(x0 - y, y0 + x, x0 + y, y0 + x, color);
    OLED_DrawLine(x0 - y, y0 - x, x0 + y, y0 - x, color);

    if (err <= 0)
    {
      y++;
      err += 2 * y + 1;
    }
    if (err > 0)
    {
      x--;
      err -= 2 * x + 1;
    }
  }
}

/**
 * @brief  绘制椭圆（中点椭圆算法）
 * @param  x0,y0 椭圆中心
 * @param  a 长轴
 * @param  b 短轴
 * @param  color 颜色
 * @retval 无
 */
void OLED_DrawEllipse(uint8_t x0, uint8_t y0, uint8_t a, uint8_t b, OLED_ColorMode color)
{
  int a2 = a * a, b2 = b * b;
  int dx = 0, dy = b;
  int err = b2 - a2 * b + a2 / 4;

  while (dx <= a2 * dy)
  {
    OLED_SetPixel(x0 + dx, y0 + dy, color);
    OLED_SetPixel(x0 - dx, y0 + dy, color);
    OLED_SetPixel(x0 - dx, y0 - dy, color);
    OLED_SetPixel(x0 + dx, y0 - dy, color);
    if (err < 0)
    {
      err += b2 * (2 * dx + 3);
    }
    else
    {
      err += b2 * (2 * dx + 3) + a2 * (-2 * dy + 2);
      dy--;
    }
    dx++;
  }

  err = b2 * (dx + 0.5) * (dx + 0.5) + a2 * (dy - 1) * (dy - 1) - a2 * b2;
  while (dy >= 0)
  {
    OLED_SetPixel(x0 + dx, y0 + dy, color);
    OLED_SetPixel(x0 - dx, y0 + dy, color);
    OLED_SetPixel(x0 - dx, y0 - dy, color);
    OLED_SetPixel(x0 + dx, y0 - dy, color);
    if (err > 0)
    {
      err += a2 * (-2 * dy + 3);
    }
    else
    {
      err += b2 * (2 * dx + 2) + a2 * (-2 * dy + 3);
      dx++;
    }
    dy--;
  }
}
